﻿// Copyright (c) .NET Foundation and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
using static Roslynator.CSharp.CSharpFactory;

namespace Roslynator.CSharp.Refactorings;

internal static class UseElementAccessInsteadOfEnumerableMethodRefactoring
{
    public static Task<Document> UseElementAccessInsteadOfElementAtAsync(
        Document document,
        InvocationExpressionSyntax invocation,
        CancellationToken cancellationToken = default)
    {
        ArgumentListSyntax argumentList = invocation.ArgumentList;

        var memberAccess = (MemberAccessExpressionSyntax)invocation.Expression;

        ExpressionSyntax expression = memberAccess.Expression;

        IEnumerable<SyntaxTrivia> trivia = memberAccess.DescendantTrivia(TextSpan.FromBounds(memberAccess.Expression.Span.End, memberAccess.Name.FullSpan.End));

        if (trivia.All(f => f.IsWhitespaceOrEndOfLineTrivia()))
        {
            expression = expression.WithoutTrailingTrivia();
        }
        else
        {
            expression = expression.WithTrailingTrivia(trivia);
        }

        ExpressionSyntax argumentExpression = argumentList.Arguments[0].Expression;

        ElementAccessExpressionSyntax elementAccess = ElementAccessExpression(
            expression,
            BracketedArgumentList(
                OpenBracketToken().WithTriviaFrom(argumentList.OpenParenToken),
                SingletonSeparatedList(Argument(argumentExpression)),
                CloseBracketToken().WithTriviaFrom(argumentList.CloseParenToken)));

        return document.ReplaceNodeAsync(invocation, elementAccess, cancellationToken);
    }

    public static Task<Document> UseElementAccessInsteadOfFirstAsync(
        Document document,
        InvocationExpressionSyntax invocation,
        CancellationToken cancellationToken = default)
    {
        ArgumentListSyntax argumentList = invocation.ArgumentList;

        var memberAccess = (MemberAccessExpressionSyntax)invocation.Expression;

        ExpressionSyntax expression = memberAccess.Expression;

        IEnumerable<SyntaxTrivia> trivia = memberAccess.DescendantTrivia(TextSpan.FromBounds(memberAccess.Expression.Span.End, memberAccess.Name.FullSpan.End));

        if (trivia.All(f => f.IsWhitespaceOrEndOfLineTrivia()))
        {
            expression = expression.WithoutTrailingTrivia();
        }
        else
        {
            expression = expression.WithTrailingTrivia(trivia);
        }

        ExpressionSyntax argumentExpression = NumericLiteralExpression(0);

        ElementAccessExpressionSyntax elementAccess = ElementAccessExpression(
            expression,
            BracketedArgumentList(
                OpenBracketToken().WithTriviaFrom(argumentList.OpenParenToken),
                SingletonSeparatedList(Argument(argumentExpression)),
                CloseBracketToken().WithTriviaFrom(argumentList.CloseParenToken)));

        return document.ReplaceNodeAsync(invocation, elementAccess, cancellationToken);
    }

    public static Task<Document> UseElementAccessInsteadOfLastAsync(
        Document document,
        InvocationExpressionSyntax invocation,
        CancellationToken cancellationToken = default)
    {
        ArgumentListSyntax argumentList = invocation.ArgumentList;

        var memberAccess = (MemberAccessExpressionSyntax)invocation.Expression;

        ExpressionSyntax expression = memberAccess.Expression;

        IEnumerable<SyntaxTrivia> trivia = memberAccess.DescendantTrivia(TextSpan.FromBounds(memberAccess.Expression.Span.End, memberAccess.Name.FullSpan.End));

        if (trivia.All(f => f.IsWhitespaceOrEndOfLineTrivia()))
        {
            expression = expression.WithoutTrailingTrivia();
        }
        else
        {
            expression = expression.WithTrailingTrivia(trivia);
        }

        ExpressionSyntax argumentExpression = ParseExpression("^1");

        ElementAccessExpressionSyntax elementAccess = ElementAccessExpression(
            expression,
            BracketedArgumentList(
                OpenBracketToken().WithTriviaFrom(argumentList.OpenParenToken),
                SingletonSeparatedList(Argument(argumentExpression)),
                CloseBracketToken().WithTriviaFrom(argumentList.CloseParenToken)));

        return document.ReplaceNodeAsync(invocation, elementAccess, cancellationToken);
    }
}
